import os
import sys
import time
import struct
import socket
from fcntl import ioctl
from select import select
from threading import Thread
from ipaddress import ip_network
from django.core.management import BaseCommand

VPN_SERVER_CONF={
    'PASSWORD': b'4fb88ca224e',
    'BIND_ADDRESS': '0.0.0.0',
    'BIND_PORT': 5010,
    'NETWORK': '192.168.255.0/24',
    'BUFFER_SIZE': 4096,
    'MTU': 1400,
}

TUNSETIFF = 0x400454ca
IFF_TUN   = 0x0001
IFF_TAP   = 0x0002

class VpnSessin():
    def __init__(self, addr = None, tunAddr = None):
        self.addr = addr
        self.tunAddr = tunAddr
        self.lastTime = 0

class VpnServer():
    def __init__(self, conf):
        self.PASSWORD = conf['PASSWORD']
        self.BIND_ADDRESS = conf['BIND_ADDRESS']
        self.BIND_PORT = conf['BIND_PORT']
        self.NETWORK = conf['NETWORK']
        self.BUFFER_SIZE = conf['BUFFER_SIZE']
        self.MTU = conf['MTU']

        self.IPRANGE = list(map(str,ip_network(self.NETWORK)))[1:]
        self.NET_MASK = '255.255.255.0'
        self.LOCAL_IP = self.IPRANGE.pop(0)

        self.tunfd, self.tunName = self.createTunnel()
        self.startTunnel(self.tunName)
        self.sessions = []
        self.readables = []
        self.udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.udp.bind((self.BIND_ADDRESS, self.BIND_PORT))
        self.readables.append(self.udp)
        self.readables.append(self.tunfd)
        
        print('Server listen on %s:%s...' % (self.BIND_ADDRESS, self.BIND_PORT))

    def createTunnel(self, tunName='tun%d',tunMode=IFF_TUN):
        tunfd = os.open("/dev/net/tun", os.O_RDWR)
        ifn = ioctl(tunfd, TUNSETIFF, struct.pack(b"16sH", tunName.encode(), tunMode))
        tunName = ifn[:16].decode().strip("\x00")
        return tunfd,tunName
    
    def startTunnel(self, tunName):
        print(os.popen('ifconfig %s %s netmask %s mtu %s up' % (tunName, self.LOCAL_IP, self.NET_MASK, self.MTU)).read())

    def getAddrByTunAddr(self, tunAddr):
        for it in self.sessions:
            if it.tunAddr == tunAddr:
                return it.addr
        return None

    def getTunAddrByAddr(self, addr):
        ses = self.getSessionByAddr(addr)
        if ses:
            return ses.tunAddr
        return None
    
    def getSessionByAddr(self, addr):
        for it in self.sessions:
            if it.addr == addr:
                return it
        return None

    def updateSessionTunAddr(self, addr, tunAddr):
        ses = self.getSessionByAddr(addr)
        if ses:
            ses.tunAddr = tunAddr
            return
        ses = VpnSessin(addr, tunAddr)
        self.sessions.append(ses)

    def dumpHex(self, data):
        sf = ''
        for it in data:
            sf +='%02x '%it
        print(sf)

    def _run_forever(self):
        while True:
            readab = select(self.readables, [], [], 1)[0]
            for r in readab:
                if r == self.udp:
                    #服务器监听远端接口
                    data, addr = self.udp.recvfrom(self.BUFFER_SIZE)
                    tunDstAddr = data[16:20]
                    tunSrcAddr = data[20:24]
                    self.updateSessionTunAddr(addr, tunSrcAddr)
                    os.write(self.tunfd, data)
                    self.dumpHex(data[:24])
                    print(f'recv from {addr} tunDstAddr={tunDstAddr} tunSrcAddr={tunSrcAddr} and data={data} write to funfd')
                else:
                    data = os.read(r, self.BUFFER_SIZE)
                    tunDstAddr = data[16:20]
                    tunSrcAddr = data[20:24]
                    self.dumpHex(data[:24])
                    print(f'recv from tunfd tunDstAddr={tunDstAddr} tunSrcAddr={tunSrcAddr} data={data}')
                    addr = self.getAddrByTunAddr(tunDstAddr)
                    if not addr:
                        print('err not found remote addr')
                        continue
                    self.udp.sendto(data, addr)
                    print(f'recv from tunfd and write to remote addr is {addr}')

    def run_forever(self):
        try:
            self._run_forever()
        except KeyboardInterrupt:
            pass 
class Command(BaseCommand):
    def handle(self, *args, **options):
        server = VpnServer(VPN_SERVER_CONF)
        server.run_forever()
